suppressPackageStartupMessages(library("ggplot2"))
theme_set(ggpubr::theme_pubr(base_size=10, legend='bottom'))
suppressPackageStartupMessages(library("DESeq2"))

#Note that we do not have Clark Scores or MIA scores for AVAST-M lymph node samples and these need to be commented out to avoid errors.

#Load the data
tissue<-c("Skin")
load(paste("~/Desktop/Melanoma/deseq2Results/tc",tissue,"EventMetNo_VS_tc",tissue,"EventMetYes_CovariateCorrection.deseq2/de.Rdata", sep=""))
#find ENSEMBL ID corresponding to GRAMD1B
geneName<-"GRAMD1B"
select<-as.character(res.annot$id[res.annot$Name==geneName])
#extract expression of this gene
expressionData <- data.frame(assay(vsd)[select, ])
#Replace ENSEMBL IDs with corresponding gene names
names(expressionData) <- geneName
stand.fun <- function(x){(x-mean(x,na.rm=TRUE))/sd(x,na.rm=TRUE)}
stdSignature<-stand.fun(expressionData$GRAMD1B)
names(stdSignature)<-rownames(expressionData) #make sure they retain the name of the samples
#Multiply by beta coefficient?
#betaCoeff <- read.table("~/Desktop/Melanoma/betaCoeff.tsv", sep = "\t", header = TRUE, quote = "")
#extract beta coefficient for the gene of interest
#betaCoeffGene <- betaCoeff[select, "EventMet_Yes_vs_No"]
#weightedSignature <- expressionData*betaCoeffGene
#standardization of the weighted signature
#stand.fun <- function(x){(x-mean(x,na.rm=TRUE))/sd(x,na.rm=TRUE)}
#stdWeightedSignature <- stand.fun(weightedSignature$GRAMD1B)
#names(stdWeightedSignature)<-rownames(weightedSignature) #make sure they retain the name of the samples

As expected the absolute values after standardization of weighted signature (stand.fun(weightedSignature$GRAMD1B)) is same as those of standardized expression values (stand.fun(expressionData$GRAMD1B)). The only difference comes from sign of the beta coefficient which is negative for GRAMD1B

#Load clinical data
clinicalData<-data.frame(colData(vsd))
clinicalData$Signature<- stdSignature[rownames(clinicalData)] #merge signature in clinical data frame

#Check association of GRAMD1B with relapse

my_comparisons <- list( c("Yes", "No"))
ggplot(clinicalData[!is.na(clinicalData$EventMet),], aes(x=EventMet, y=Signature))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
  #scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
  xlab("Distant metastases (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")#+ # Add pairwise comparisons p-value

  #ggpubr::stat_compare_means(label.y = 6, method = "anova", size = 2.5, family="sans")#+

Find suitable cut-off. After talking to Roy, it seems that we could go for a weighted GRAMD1B value of 0? Let’s check the median value. May be I can take the median cut-off and perform chi-squared test to check if there is no difference between the mean of 2 groups?

#Divide in high/low groups based on mean and median
co_mean<-mean(clinicalData$Signature)
co_median<-median(clinicalData$Signature)
co_0_33<-quantile(clinicalData$Signature, 0.33)
co_0_66<-quantile(clinicalData$Signature, 0.66)
if (tissue=="Skin") {
  co_density_intersection<--0.223156179#0.235202939
}else{
  co_density_intersection<-0.3481057115#-0.223156179#0.235202939
}
suppressPackageStartupMessages(library(plotly))
p<-ggplot(data = clinicalData, aes(x = Signature, fill=EventMet)) +
  geom_density(alpha=0.3)+
  geom_vline(xintercept=c(co_mean, co_median, co_0_33, co_0_66, co_density_intersection), linetype=c('twodash', 'longdash', 'dotted', 'dotdash', 'dashed'))+
  ggtitle(tissue)
ggplotly(p) 
clinicalData$SignatureGroupMean <- as.factor(ifelse(clinicalData$Signature >= as.numeric(co_mean), "High", "Low"))
clinicalData$SignatureGroupMedian <- as.factor(ifelse(clinicalData$Signature >= as.numeric(co_median), "High", "Low"))
clinicalData$SignatureGroup0_33 <- as.factor(ifelse(clinicalData$Signature >= as.numeric(co_0_33), "High", "Low"))
clinicalData$SignatureGroup0_66 <- as.factor(ifelse(clinicalData$Signature >= as.numeric(co_0_66), "High", "Low"))
clinicalData$SignatureGroupDensityIntersection <- as.factor(ifelse(clinicalData$Signature >= as.numeric(co_density_intersection), "High", "Low"))

print(chisq.test(clinicalData$SignatureGroupMean, clinicalData$EventMet))

    Pearson's Chi-squared test with Yates' continuity correction

data:  clinicalData$SignatureGroupMean and clinicalData$EventMet
X-squared = 9.0843, df = 1, p-value = 0.002578
print(chisq.test(clinicalData$SignatureGroupMedian, clinicalData$EventMet))

    Pearson's Chi-squared test with Yates' continuity correction

data:  clinicalData$SignatureGroupMedian and clinicalData$EventMet
X-squared = 8.3039, df = 1, p-value = 0.003956
print(chisq.test(clinicalData$SignatureGroup0_33, clinicalData$EventMet))

    Pearson's Chi-squared test with Yates' continuity correction

data:  clinicalData$SignatureGroup0_33 and clinicalData$EventMet
X-squared = 13.838, df = 1, p-value = 0.0001992
print(chisq.test(clinicalData$SignatureGroup0_66, clinicalData$EventMet))

    Pearson's Chi-squared test with Yates' continuity correction

data:  clinicalData$SignatureGroup0_66 and clinicalData$EventMet
X-squared = 8.8431, df = 1, p-value = 0.002942
print(chisq.test(clinicalData$SignatureGroupDensityIntersection, clinicalData$EventMet))

    Pearson's Chi-squared test with Yates' continuity correction

data:  clinicalData$SignatureGroupDensityIntersection and clinicalData$EventMet
X-squared = 9.852, df = 1, p-value = 0.001696

#Check association of NRAS with other clinical covariates

print(chisq.test(clinicalData$NRAS, clinicalData$EventMet))

    Pearson's Chi-squared test with Yates' continuity correction

data:  clinicalData$NRAS and clinicalData$EventMet
X-squared = 2.5747, df = 1, p-value = 0.1086
print(chisq.test(clinicalData$NRAS, clinicalData$Bres))

    Pearson's Chi-squared test

data:  clinicalData$NRAS and clinicalData$Bres
X-squared = 3.0461, df = 2, p-value = 0.2181
print(chisq.test(clinicalData$NRAS, clinicalData$Ulc))

    Pearson's Chi-squared test with Yates' continuity correction

data:  clinicalData$NRAS and clinicalData$Ulc
X-squared = 0.16538, df = 1, p-value = 0.6843

#Check association of GRAMD1B with ulceration

my_comparisons <- list( c("Present", "Absent"))
ggplot(clinicalData[!is.na(clinicalData$Ulc),], aes(x=Ulc, y=Signature))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
  #scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
  xlab("Ulc (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")#+ # Add pairwise comparisons p-value

  #ggpubr::stat_compare_means(label.y = 6, method = "anova", size = 2.5, family="sans")#+

#Check association of GRAMD1B with Bres

my_comparisons <- list( c("<= 2.0 mm", ">2-4mm"), c("<= 2.0 mm", ">4.0mm"), c(">2-4mm", ">4.0mm"))
ggplot(clinicalData[!is.na(clinicalData$Bres),], aes(x=Bres, y=Signature))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
  #scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
  xlab("Bres (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")+ # Add pairwise comparisons p-value
  ggpubr::stat_compare_means(label.y = 6, method = "anova", size = 2.5, family="sans")#+

#Check association of GRAMD1B with Stage

my_comparisons <- list( c("IIB", "IIC"), c("IIB", "IIIA"), c("IIB", "IIIB"), c("IIB", "IIIC"),
                        c("IIC", "IIIA"), c("IIC", "IIIB"), c("IIC", "IIIC"),
                        c("IIIA", "IIIB"), c("IIIA", "IIIC"),
                        c("IIIB", "IIIC"))
ggplot(clinicalData[!is.na(clinicalData$Stage),], aes(x=Stage, y=Signature))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
  #scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
  xlab("Stage (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")+ # Add pairwise comparisons p-value
  ggpubr::stat_compare_means(label.y = 9, method = "anova", size = 2.5, family="sans")#+

ggplot(clinicalData[!is.na(clinicalData$Stage_binary),], aes(x=Stage_binary, y=Signature))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
  #scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
  xlab("Stage (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(method = "t.test", size = 2.5, family="sans")#+ # Add pairwise comparisons p-value

  #ggpubr::stat_compare_means(label.y = 9, method = "anova", size = 2.5, family="sans")

#Check association of GRAMD1B with Site

my_comparisons <- list( c("Head and neck", "Lower lims"), c("Head and neck", "Trunk"), c("Head and neck", "Upper limbs"),
                        c("Lower lims", "Trunk"), c("Lower lims", "Upper limbs"),
                        c("Trunk", "Upper limbs"))
ggplot(clinicalData[!is.na(clinicalData$Site),], aes(x=Site, y=Signature))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
  #scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
  xlab("Site (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")+ # Add pairwise comparisons p-value
  ggpubr::stat_compare_means(label.y = 9, method = "anova", size = 2.5, family="sans")#+

#Check association of GRAMD1B with BRAF mutation

my_comparisons <- list( c("V600E", "WT"))
ggplot(clinicalData[!is.na(clinicalData$BRAF),], aes(x=BRAF, y=Signature))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
  #scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
  xlab("BRAF (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")#+ # Add pairwise comparisons p-value

  #ggpubr::stat_compare_means(label.y = 9, method = "anova", size = 2.5, family="sans")#+

#Check association of GRAMD1B with NRAS mutation

my_comparisons <- list( c("Mutant", "WT"))
ggplot(clinicalData[!is.na(clinicalData$NRAS),], aes(x=NRAS, y=Signature))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
  #scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
  xlab("NRAS (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")#+ # Add pairwise comparisons p-value

  #ggpubr::stat_compare_means(label.y = 9, method = "anova", size = 2.5, family="sans")#+

#Check association of GRAMD1B with TIL counts

jt_ns_sm_scores = read.table("../../../JT_NS_SM_Combined.xls - JT_NS_SM.tsv", sep = "\t", header = TRUE, quote = "")
combinedScore <- data.frame("Scanned_file.me"= jt_ns_sm_scores$JT_Scanned_file.me, 
                                "R.Seq_sampleID"= jt_ns_sm_scores$JT_R.Seq_sampleID, 
                                "ClarkScore"= ifelse(jt_ns_sm_scores$JT_Clark_score==jt_ns_sm_scores$NS_Clark_score,
                            jt_ns_sm_scores$JT_Clark_score, 
                            jt_ns_sm_scores$SM_Clark_score),
                           "MIAScore"= ifelse(jt_ns_sm_scores$JT_TIL_grade==jt_ns_sm_scores$NS_TIL.GRADE,
                            jt_ns_sm_scores$JT_TIL_grade, 
                            jt_ns_sm_scores$SM_TIL_grade))
#Remove duplicated entries
combinedScore<-combinedScore[!duplicated(combinedScore$R.Seq_sampleID), ]
rownames(combinedScore)<-combinedScore$R.Seq_sampleID #rename rownames with sample ids
rownames(clinicalData)<-clinicalData$RNA.Seq.Sample
df<-data.frame("EventMet"=clinicalData$EventMet,
               "ClarkScore"=combinedScore[rownames(clinicalData), "ClarkScore"],
               "MIAScore"=combinedScore[rownames(clinicalData), "MIAScore"],
               "Signature"=clinicalData$Signature)
rownames(df)<-rownames(clinicalData)

df$ClarkScore <- as.factor(df$ClarkScore)
levels(df$ClarkScore) <- c("absent", "nonbrisk", "brisk")
my_comparisons <- list( c("absent", "nonbrisk"), c("nonbrisk", "brisk"), c("absent", "brisk") )
#df$JT_Clark_score <- factor(df$JT_Clark_score, levels = c("absent", "nonbrisk", "brisk"))
ggplot(df[!is.na(df$ClarkScore), ], aes(x=ClarkScore, y=Signature#, color=DRBrain
                                        ))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
  #scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
  xlab("Clark score (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(df$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")+ # Add pairwise comparisons p-value
  ggpubr::stat_compare_means(label.y = 6, method = "anova", size = 2.5, family="sans")#+

 # facet_wrap(~DRBrain)# Add global p-value
my_comparisons <- list( c("0", "1"), c("0", "2"), c("0", "3"), c("1", "2"), c("1", "3"), c("2", "3"))
df$MIAScore <- as.factor(df$MIAScore)
ggplot(df[!is.na(df$MIAScore), ], aes(x=MIAScore, y=Signature#, color=DRBrain
                                      ))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5)+
  scale_fill_brewer(type="qual", palette = "Dark2", name = "MIA score")+
  xlab("MIA score (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(df$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")+ # Add pairwise comparisons p-value
  ggpubr::stat_compare_means(label.y = 6, method = "anova", size = 2.5, family="sans")#+

  #facet_wrap(~DRBrain)
     # Add global p-value
#my_comparisons <- list( c("Mutant", "WT"))
ggplot(clinicalData[!is.na(clinicalData$treatment),], aes(x=treatment, y=Signature))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
  #scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
  xlab("Treatment (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(method = "t.test", size = 2.5, family="sans")#+ # Add pairwise comparisons p-value

  #ggpubr::stat_compare_means(label.y = 9, method = "anova", size = 2.5, family="sans")#+
#my_comparisons <- list( c("Mutant", "WT"))
ggplot(clinicalData[!is.na(clinicalData$ECOG),], aes(x=ECOG, y=Signature))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
  #scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
  xlab("ECOG (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(method = "t.test", size = 2.5, family="sans")#+ # Add pairwise comparisons p-value

  #ggpubr::stat_compare_means(label.y = 9, method = "anova", size = 2.5, family="sans")#+

#Perform differential expression analysis

combinedScore$ClarkScore <- as.factor(combinedScore$ClarkScore)
levels(combinedScore$ClarkScore) <- c("absent", "nonbrisk", "brisk")
clinicalData$ClarkScore<-combinedScore[rownames(clinicalData), "ClarkScore"]
#merge(clinicalData, combinedScore, by.x="RNA.Seq.Sample", by.y="R.Seq_sampleID")
#rownames(clinicalData)<-clinicalData$RNA.Seq.Sample
#clinicalData$ClarkScore=combinedScore$ClarkScore[rownames(clinicalData)]
#While accounting for Stage, NRAS mutation, TIL score and EventMet
#Subset to only those samples which have non-missing entires for these covariates
clinicalData_sub<-clinicalData[(!is.na(clinicalData$Stage))&(!is.na(clinicalData$NRAS))&(!is.na(clinicalData$ClarkScore))&(!is.na(clinicalData$EventMet)), ]
data.f_sub<-data.f[, rownames(clinicalData_sub)]
table(clinicalData_sub$SignatureGroupMean)

High  Low 
  34   32 
dds1 <- DESeqDataSetFromMatrix(countData = data.f_sub,
                               colData   = clinicalData_sub,
                               design    = ~ Stage+NRAS+ClarkScore+EventMet+SignatureGroupMean)
totalcounts1.gene = rowSums(counts(dds1))
dds1 <- dds1[totalcounts1.gene>=10,]
# dimensions post filtering
m = nrow(dds1)
n = ncol(dds1)
dds1 <- DESeq(dds1)
using pre-existing size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
11 rows did not converge in beta, labelled in mcols(object)$betaConv. Use larger maxit argument with nbinomWaldTest
res1 <- results(dds1)
res1
log2 fold change (MLE): SignatureGroupMean Low vs High 
Wald test p-value: SignatureGroupMean Low vs High 
DataFrame with 37079 rows and 6 columns
                 baseMean log2FoldChange     lfcSE        stat     pvalue      padj
                <numeric>      <numeric> <numeric>   <numeric>  <numeric> <numeric>
ENSG00000000003   861.278     0.11366862 0.1629208  0.69769243 0.48536957 0.8133089
ENSG00000000005    33.432    -0.69252919 0.4546602 -1.52317957 0.12771379 0.4931014
ENSG00000000419  5709.545    -0.00041328 0.1010882 -0.00408831 0.99673801 0.9993295
ENSG00000000457  1391.790     0.16335820 0.0957011  1.70696327 0.08782888 0.4188545
ENSG00000000460  1956.996     0.39953052 0.1421792  2.81004990 0.00495338 0.0840005
...                   ...            ...       ...         ...        ...       ...
ENSG00000284738  6.408754       0.272156  0.431137    0.631254  0.5278747  0.835093
ENSG00000284740  6.870408       0.591347  0.350720    1.686092  0.0917782  0.425316
ENSG00000284744 22.946725      -0.591098  0.374356   -1.578974  0.1143421  0.470308
ENSG00000284747  6.877460      -0.574416  0.327618   -1.753310  0.0795488  0.399437
ENSG00000284748  0.235384      -0.320125  1.496303   -0.213944  0.8305906        NA
#Merge with gene names and check if the differentially expressed genes make sense
res<-data.frame(res1)
res<-res[order(res$padj), ]
res$Name<-res.annot[rownames(res), "Name"]
head(res)
#GRAMD1B is differentially expressed in GRAMD1B groups and is downregulated in the low expression group compared to the high-expression group so atleast this makes sense.

#Perform lfc shrinkage for GSEA

library("apeglm")
coefName = resultsNames(dds1)
resWithLfcShrinkage = data.frame(lfcShrink(dds1, coef = tail(coefName, n=1), type="apeglm"))
using 'apeglm' for LFC shrinkage. If used in published research, please cite:
    Zhu, A., Ibrahim, J.G., Love, M.I. (2018) Heavy-tailed prior distributions for
    sequence count data: removing the noise and preserving large differences.
    Bioinformatics. https://doi.org/10.1093/bioinformatics/bty895
#Add suffix to colnames for easy identification later on.
colnames(resWithLfcShrinkage) = paste(colnames(resWithLfcShrinkage),"lfcShrinkApplied", sep = "_")
#Add ENSEMBL ID as a column in the data frame to ease merging later
resWithLfcShrinkage$id = rownames(resWithLfcShrinkage)
#Merge these with DE original DE results
res$id<-rownames(res)
deResultsUpdated = merge(x = res, y = data.frame(resWithLfcShrinkage), by = "id")
deResultsUpdated = deResultsUpdated[order(deResultsUpdated$padj),]
#Save the results
write.table(deResultsUpdated, file = "../results/de_gsea/deWithLfcSkrinkageApplied_GRAMD1B_all.tsv", col.names = TRUE, row.names = FALSE, quote = FALSE, sep = '\t')
#Save the differentially expressed genes (padj<0.1)
write.table(deResultsUpdated[deResultsUpdated$padj_lfcShrinkApplied<0.1, ], file = "../results/de_gsea/deWithLfcSkrinkageApplied_GRAMD1B_padj_0_1.tsv", col.names = TRUE, row.names = FALSE, quote = FALSE, sep = '\t')
head(deResultsUpdated)

#Create a ranked list for GSEA

#Create ranked list for running Pre-ranked GSEA tool
deResultsUpdated<-read.table("../results/de_gsea/deWithLfcSkrinkageApplied_GRAMD1B_all.tsv", header = T, sep = "\t", quote = "")
rankedList = na.omit(data.frame(deResultsUpdated$Name, deResultsUpdated$log2FoldChange_lfcShrinkApplied))
rankedList = rankedList[with(rankedList, order(deResultsUpdated.log2FoldChange_lfcShrinkApplied, decreasing = T)), ]
#Save the ranked list to a new file for GSEA.
write.table(rankedList, file = "../results/de_gsea/rankedList_GRAMD1B.rnk", col.names = FALSE, row.names = FALSE, quote = FALSE, sep = '\t')

#Plot survival curves between the continuous signature and the two groups ## First add necesssary columns

suppressPackageStartupMessages(library("survival"))
# survival outcome 1: "d" for death and "ltrc" for left truncated and right censored
clinicalData$survival_d_ltrc = Surv(time  = as.numeric(difftime(clinicalData$DOE, clinicalData$DDiag))/365.25,
                                    time2 = as.numeric(difftime(clinicalData$DOC, clinicalData$DDiag))/365.25,
                                    event = ifelse(clinicalData$Dead == FALSE, 0, 1))

# survival outcome 2: "rd" for relapse or death, "ltrc" for left truncated and right censored
clinicalData$survival_rd_ltrc = Surv(time  = as.numeric(difftime(clinicalData$DOE, clinicalData$DDiag))/365.25,
                                     time2 = as.numeric(difftime(apply(clinicalData[, c("DOC", "DDistMets")],1,min,na.rm=TRUE), clinicalData$DDiag))/365.25,
                                     event = ifelse((clinicalData$Dead == FALSE & clinicalData$EventMet == "No"), 0, 1))

if(tissue=="Skin"){
  jt_ns_sm_scores = read.table("../JT_NS_SM_Combined.xls - JT_NS_SM.tsv", sep = "\t", header = TRUE, quote = "")
  combinedScore <- data.frame("Scanned_file.me"= jt_ns_sm_scores$JT_Scanned_file.me, 
                              "R.Seq_sampleID"= jt_ns_sm_scores$JT_R.Seq_sampleID, 
                              "ClarkScore"=ifelse(jt_ns_sm_scores$JT_Clark_score==jt_ns_sm_scores$NS_Clark_score,
                                                  jt_ns_sm_scores$JT_Clark_score, 
                                                  jt_ns_sm_scores$SM_Clark_score),
                           "MIAScore"= ifelse(jt_ns_sm_scores$JT_TIL_grade==jt_ns_sm_scores$NS_TIL.GRADE,
                                              jt_ns_sm_scores$JT_TIL_grade, 
                                              jt_ns_sm_scores$SM_TIL_grade))
  #Remove duplicated entries
  combinedScore<-combinedScore[!duplicated(combinedScore$R.Seq_sampleID), ]
  rownames(combinedScore)<-combinedScore$R.Seq_sampleID #rename rownames with sample ids
  combinedScore$ClarkScore <- as.factor(combinedScore$ClarkScore)
  levels(combinedScore$ClarkScore) <- c("absent", "nonbrisk", "brisk")
  clinicalData<-merge(clinicalData, combinedScore, by.x="RNA.Seq.Sample", by.y="R.Seq_sampleID")
  rownames(clinicalData)<-clinicalData$RNA.Seq.Sample
}

then calculate the values

os<-data.frame()
pfs<-data.frame()
temp<-data.frame()

combinations<-c("Signature", "SignatureGroupMean", "SignatureGroupMedian", "SignatureGroup0_33", "SignatureGroup0_66", "SignatureGroupDensityIntersection")

for (combination in combinations) {
  if (tissue=="Skin") {
    os_formula<-paste0("survival_d_ltrc~",combination,"+Sex+Age+as.numeric(Nclass)+as.character(Stage)+ECOG+treatment+ClarkScore")
    pfs_formula<-paste0("survival_rd_ltrc~",combination,"+Sex+Age+as.numeric(Nclass)+as.character(Stage)+ECOG+treatment+ClarkScore")
  } else{
    os_formula<-paste0("survival_d_ltrc~",combination,"+Sex+Age+as.numeric(Nclass)+as.character(Stage)+ECOG+treatment")
    pfs_formula<-paste0("survival_rd_ltrc~",combination,"+Sex+Age+as.numeric(Nclass)+as.character(Stage)+ECOG+treatment")
  }
  
  fit = coef(summary(coxph(as.formula(os_formula), data=clinicalData)))
  mid  = fit[1,c("exp(coef)")]
  low  = exp(fit[1,c("coef")]-
              qnorm(.975)*fit[1,c("se(coef)")])                   
  high = exp(fit[1,c("coef")]+
              qnorm(.975)*fit[1,c("se(coef)")])  
  pval = fit[1,c("Pr(>|z|)")]
  temp["Signature",c("Signature", "HR","low","high","pval")] = c(rownames(fit)[1],mid,low,high,pval)
  os<-rbind(os, temp)
  
  fit = coef(summary(coxph(as.formula(pfs_formula), data=clinicalData)))
  mid  = fit[1,c("exp(coef)")]
  low  = exp(fit[1,c("coef")]-
              qnorm(.975)*fit[1,c("se(coef)")])                   
  high = exp(fit[1,c("coef")]+
              qnorm(.975)*fit[1,c("se(coef)")])  
  pval = fit[1,c("Pr(>|z|)")]
  temp["Signature",c("Signature", "HR","low","high","pval")] = c(rownames(fit)[1],mid,low,high,pval)
  pfs<-rbind(pfs, temp)
}

write.table(os, paste("../results/survival/AVAST-M_",tissue,"_os.tsv", sep=""), sep="\t", col.names = T, row.names = F)
write.table(pfs, paste("../results/survival/AVAST-M_",tissue,"_pfs.tsv", sep=""), sep="\t", col.names = T, row.names = F)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KCJnZ3Bsb3QyIikpCnRoZW1lX3NldChnZ3B1YnI6OnRoZW1lX3B1YnIoYmFzZV9zaXplPTEwLCBsZWdlbmQ9J2JvdHRvbScpKQpgYGAKCmBgYHtyfQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSgiREVTZXEyIikpCmBgYAoKI05vdGUgdGhhdCB3ZSBkbyBub3QgaGF2ZSBDbGFyayBTY29yZXMgb3IgTUlBIHNjb3JlcyBmb3IgQVZBU1QtTSBseW1waCBub2RlIHNhbXBsZXMgYW5kIHRoZXNlIG5lZWQgdG8gYmUgY29tbWVudGVkIG91dCB0byBhdm9pZCBlcnJvcnMuIApgYGB7cn0KI0xvYWQgdGhlIGRhdGEKdGlzc3VlPC1jKCJTa2luIikKbG9hZChwYXN0ZSgifi9EZXNrdG9wL01lbGFub21hL2Rlc2VxMlJlc3VsdHMvdGMiLHRpc3N1ZSwiRXZlbnRNZXROb19WU190YyIsdGlzc3VlLCJFdmVudE1ldFllc19Db3ZhcmlhdGVDb3JyZWN0aW9uLmRlc2VxMi9kZS5SZGF0YSIsIHNlcD0iIikpCmBgYAoKYGBge3J9CiNmaW5kIEVOU0VNQkwgSUQgY29ycmVzcG9uZGluZyB0byBHUkFNRDFCCmdlbmVOYW1lPC0iR1JBTUQxQiIKc2VsZWN0PC1hcy5jaGFyYWN0ZXIocmVzLmFubm90JGlkW3Jlcy5hbm5vdCROYW1lPT1nZW5lTmFtZV0pCiNleHRyYWN0IGV4cHJlc3Npb24gb2YgdGhpcyBnZW5lCmV4cHJlc3Npb25EYXRhIDwtIGRhdGEuZnJhbWUoYXNzYXkodnNkKVtzZWxlY3QsIF0pCiNSZXBsYWNlIEVOU0VNQkwgSURzIHdpdGggY29ycmVzcG9uZGluZyBnZW5lIG5hbWVzCm5hbWVzKGV4cHJlc3Npb25EYXRhKSA8LSBnZW5lTmFtZQpzdGFuZC5mdW4gPC0gZnVuY3Rpb24oeCl7KHgtbWVhbih4LG5hLnJtPVRSVUUpKS9zZCh4LG5hLnJtPVRSVUUpfQpzdGRTaWduYXR1cmU8LXN0YW5kLmZ1bihleHByZXNzaW9uRGF0YSRHUkFNRDFCKQpuYW1lcyhzdGRTaWduYXR1cmUpPC1yb3duYW1lcyhleHByZXNzaW9uRGF0YSkgI21ha2Ugc3VyZSB0aGV5IHJldGFpbiB0aGUgbmFtZSBvZiB0aGUgc2FtcGxlcwpgYGAKCmBgYHtyfQojTXVsdGlwbHkgYnkgYmV0YSBjb2VmZmljaWVudD8KI2JldGFDb2VmZiA8LSByZWFkLnRhYmxlKCJ+L0Rlc2t0b3AvTWVsYW5vbWEvYmV0YUNvZWZmLnRzdiIsIHNlcCA9ICJcdCIsIGhlYWRlciA9IFRSVUUsIHF1b3RlID0gIiIpCiNleHRyYWN0IGJldGEgY29lZmZpY2llbnQgZm9yIHRoZSBnZW5lIG9mIGludGVyZXN0CiNiZXRhQ29lZmZHZW5lIDwtIGJldGFDb2VmZltzZWxlY3QsICJFdmVudE1ldF9ZZXNfdnNfTm8iXQojd2VpZ2h0ZWRTaWduYXR1cmUgPC0gZXhwcmVzc2lvbkRhdGEqYmV0YUNvZWZmR2VuZQojc3RhbmRhcmRpemF0aW9uIG9mIHRoZSB3ZWlnaHRlZCBzaWduYXR1cmUKI3N0YW5kLmZ1biA8LSBmdW5jdGlvbih4KXsoeC1tZWFuKHgsbmEucm09VFJVRSkpL3NkKHgsbmEucm09VFJVRSl9CiNzdGRXZWlnaHRlZFNpZ25hdHVyZSA8LSBzdGFuZC5mdW4od2VpZ2h0ZWRTaWduYXR1cmUkR1JBTUQxQikKI25hbWVzKHN0ZFdlaWdodGVkU2lnbmF0dXJlKTwtcm93bmFtZXMod2VpZ2h0ZWRTaWduYXR1cmUpICNtYWtlIHN1cmUgdGhleSByZXRhaW4gdGhlIG5hbWUgb2YgdGhlIHNhbXBsZXMKYGBgCgpBcyBleHBlY3RlZCB0aGUgYWJzb2x1dGUgdmFsdWVzIGFmdGVyIHN0YW5kYXJkaXphdGlvbiBvZiB3ZWlnaHRlZCBzaWduYXR1cmUgKHN0YW5kLmZ1bih3ZWlnaHRlZFNpZ25hdHVyZVwkR1JBTUQxQikpIGlzIHNhbWUgYXMgdGhvc2Ugb2Ygc3RhbmRhcmRpemVkIGV4cHJlc3Npb24gdmFsdWVzIChzdGFuZC5mdW4oZXhwcmVzc2lvbkRhdGFcJEdSQU1EMUIpKS4gVGhlIG9ubHkgZGlmZmVyZW5jZSBjb21lcyBmcm9tIHNpZ24gb2YgdGhlIGJldGEgY29lZmZpY2llbnQgd2hpY2ggaXMgbmVnYXRpdmUgZm9yIEdSQU1EMUIKCmBgYHtyfQojTG9hZCBjbGluaWNhbCBkYXRhCmNsaW5pY2FsRGF0YTwtZGF0YS5mcmFtZShjb2xEYXRhKHZzZCkpCmNsaW5pY2FsRGF0YSRTaWduYXR1cmU8LSBzdGRTaWduYXR1cmVbcm93bmFtZXMoY2xpbmljYWxEYXRhKV0gI21lcmdlIHNpZ25hdHVyZSBpbiBjbGluaWNhbCBkYXRhIGZyYW1lCmBgYAoKI0NoZWNrIGFzc29jaWF0aW9uIG9mIEdSQU1EMUIgd2l0aCByZWxhcHNlCmBgYHtyfQpteV9jb21wYXJpc29ucyA8LSBsaXN0KCBjKCJZZXMiLCAiTm8iKSkKZ2dwbG90KGNsaW5pY2FsRGF0YVshaXMubmEoY2xpbmljYWxEYXRhJEV2ZW50TWV0KSxdLCBhZXMoeD1FdmVudE1ldCwgeT1TaWduYXR1cmUpKSsKICBnZW9tX3Zpb2xpbigpKwogIGdlb21faml0dGVyKGhlaWdodCA9IDAsIHdpZHRoID0gMC4xLCBhbHBoYSA9IDAuNSwgYWVzKCkpKwogICNzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJxdWFsIiwgcGFsZXR0ZSA9ICJEYXJrMiIsIG5hbWUgPSAiQ2FtXzEyMSByaXNrIGdyb3VwIikrCiAgeGxhYigiRGlzdGFudCBtZXRhc3Rhc2VzIChBVkFTVC1NIFNraW4pIikrCiAgeWxhYigiVlNUIG5vcm1hbGl6ZWQgR1JBTUQxQiBleHByZXNzaW9uIChzdGFuZC4pIikrCiAgI2dlb21faGxpbmUoeWludGVyY2VwdCA9IHF1YW50aWxlKGNsaW5pY2FsRGF0YSRTaWduYXR1cmUsIDAuNzUpLCBjb2xvciA9ICJncmV5IiwgbGluZXR5cGUgPSAibG9uZ2Rhc2giKSsKICB0aGVtZSh0ZXh0PWVsZW1lbnRfdGV4dChzaXplPTcsICBmYW1pbHk9InNhbnMiKSkrCiAgZ2dwdWJyOjpzdGF0X2NvbXBhcmVfbWVhbnMoY29tcGFyaXNvbnMgPSBteV9jb21wYXJpc29ucywgbWV0aG9kID0gInQudGVzdCIsIHNpemUgPSAyLjUsIGZhbWlseT0ic2FucyIpIysgIyBBZGQgcGFpcndpc2UgY29tcGFyaXNvbnMgcC12YWx1ZQogICNnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhsYWJlbC55ID0gNiwgbWV0aG9kID0gImFub3ZhIiwgc2l6ZSA9IDIuNSwgZmFtaWx5PSJzYW5zIikjKwoKYGBgCkZpbmQgc3VpdGFibGUgY3V0LW9mZi4gQWZ0ZXIgdGFsa2luZyB0byBSb3ksIGl0IHNlZW1zIHRoYXQgd2UgY291bGQgZ28gZm9yIGEgd2VpZ2h0ZWQgR1JBTUQxQiB2YWx1ZSBvZiAwPwpMZXQncyBjaGVjayB0aGUgbWVkaWFuIHZhbHVlLiBNYXkgYmUgSSBjYW4gdGFrZSB0aGUgbWVkaWFuIGN1dC1vZmYgYW5kIHBlcmZvcm0gY2hpLXNxdWFyZWQgdGVzdCB0byBjaGVjayBpZiB0aGVyZSBpcyBubyBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIG1lYW4gb2YgMiBncm91cHM/CmBgYHtyfQojRGl2aWRlIGluIGhpZ2gvbG93IGdyb3VwcyBiYXNlZCBvbiBtZWFuIGFuZCBtZWRpYW4KY29fbWVhbjwtbWVhbihjbGluaWNhbERhdGEkU2lnbmF0dXJlKQpjb19tZWRpYW48LW1lZGlhbihjbGluaWNhbERhdGEkU2lnbmF0dXJlKQpjb18wXzMzPC1xdWFudGlsZShjbGluaWNhbERhdGEkU2lnbmF0dXJlLCAwLjMzKQpjb18wXzY2PC1xdWFudGlsZShjbGluaWNhbERhdGEkU2lnbmF0dXJlLCAwLjY2KQppZiAodGlzc3VlPT0iU2tpbiIpIHsKICBjb19kZW5zaXR5X2ludGVyc2VjdGlvbjwtLTAuMjIzMTU2MTc5IzAuMjM1MjAyOTM5Cn1lbHNlewogIGNvX2RlbnNpdHlfaW50ZXJzZWN0aW9uPC0wLjM0ODEwNTcxMTUjLTAuMjIzMTU2MTc5IzAuMjM1MjAyOTM5Cn0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkocGxvdGx5KSkKcDwtZ2dwbG90KGRhdGEgPSBjbGluaWNhbERhdGEsIGFlcyh4ID0gU2lnbmF0dXJlLCBmaWxsPUV2ZW50TWV0KSkgKwogIGdlb21fZGVuc2l0eShhbHBoYT0wLjMpKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD1jKGNvX21lYW4sIGNvX21lZGlhbiwgY29fMF8zMywgY29fMF82NiwgY29fZGVuc2l0eV9pbnRlcnNlY3Rpb24pLCBsaW5ldHlwZT1jKCd0d29kYXNoJywgJ2xvbmdkYXNoJywgJ2RvdHRlZCcsICdkb3RkYXNoJywgJ2Rhc2hlZCcpKSsKICBnZ3RpdGxlKHRpc3N1ZSkKZ2dwbG90bHkocCkgCmBgYAoKYGBge3J9CmNsaW5pY2FsRGF0YSRTaWduYXR1cmVHcm91cE1lYW4gPC0gYXMuZmFjdG9yKGlmZWxzZShjbGluaWNhbERhdGEkU2lnbmF0dXJlID49IGFzLm51bWVyaWMoY29fbWVhbiksICJIaWdoIiwgIkxvdyIpKQpjbGluaWNhbERhdGEkU2lnbmF0dXJlR3JvdXBNZWRpYW4gPC0gYXMuZmFjdG9yKGlmZWxzZShjbGluaWNhbERhdGEkU2lnbmF0dXJlID49IGFzLm51bWVyaWMoY29fbWVkaWFuKSwgIkhpZ2giLCAiTG93IikpCmNsaW5pY2FsRGF0YSRTaWduYXR1cmVHcm91cDBfMzMgPC0gYXMuZmFjdG9yKGlmZWxzZShjbGluaWNhbERhdGEkU2lnbmF0dXJlID49IGFzLm51bWVyaWMoY29fMF8zMyksICJIaWdoIiwgIkxvdyIpKQpjbGluaWNhbERhdGEkU2lnbmF0dXJlR3JvdXAwXzY2IDwtIGFzLmZhY3RvcihpZmVsc2UoY2xpbmljYWxEYXRhJFNpZ25hdHVyZSA+PSBhcy5udW1lcmljKGNvXzBfNjYpLCAiSGlnaCIsICJMb3ciKSkKY2xpbmljYWxEYXRhJFNpZ25hdHVyZUdyb3VwRGVuc2l0eUludGVyc2VjdGlvbiA8LSBhcy5mYWN0b3IoaWZlbHNlKGNsaW5pY2FsRGF0YSRTaWduYXR1cmUgPj0gYXMubnVtZXJpYyhjb19kZW5zaXR5X2ludGVyc2VjdGlvbiksICJIaWdoIiwgIkxvdyIpKQoKcHJpbnQoY2hpc3EudGVzdChjbGluaWNhbERhdGEkU2lnbmF0dXJlR3JvdXBNZWFuLCBjbGluaWNhbERhdGEkRXZlbnRNZXQpKQpwcmludChjaGlzcS50ZXN0KGNsaW5pY2FsRGF0YSRTaWduYXR1cmVHcm91cE1lZGlhbiwgY2xpbmljYWxEYXRhJEV2ZW50TWV0KSkKcHJpbnQoY2hpc3EudGVzdChjbGluaWNhbERhdGEkU2lnbmF0dXJlR3JvdXAwXzMzLCBjbGluaWNhbERhdGEkRXZlbnRNZXQpKQpwcmludChjaGlzcS50ZXN0KGNsaW5pY2FsRGF0YSRTaWduYXR1cmVHcm91cDBfNjYsIGNsaW5pY2FsRGF0YSRFdmVudE1ldCkpCnByaW50KGNoaXNxLnRlc3QoY2xpbmljYWxEYXRhJFNpZ25hdHVyZUdyb3VwRGVuc2l0eUludGVyc2VjdGlvbiwgY2xpbmljYWxEYXRhJEV2ZW50TWV0KSkKYGBgCiNDaGVjayBhc3NvY2lhdGlvbiBvZiBOUkFTIHdpdGggb3RoZXIgY2xpbmljYWwgY292YXJpYXRlcwpgYGB7cn0KcHJpbnQoY2hpc3EudGVzdChjbGluaWNhbERhdGEkTlJBUywgY2xpbmljYWxEYXRhJEV2ZW50TWV0KSkKcHJpbnQoY2hpc3EudGVzdChjbGluaWNhbERhdGEkTlJBUywgY2xpbmljYWxEYXRhJEJyZXMpKQpwcmludChjaGlzcS50ZXN0KGNsaW5pY2FsRGF0YSROUkFTLCBjbGluaWNhbERhdGEkVWxjKSkKYGBgCiNDaGVjayBhc3NvY2lhdGlvbiBvZiBHUkFNRDFCIHdpdGggdWxjZXJhdGlvbgpgYGB7cn0KbXlfY29tcGFyaXNvbnMgPC0gbGlzdCggYygiUHJlc2VudCIsICJBYnNlbnQiKSkKZ2dwbG90KGNsaW5pY2FsRGF0YVshaXMubmEoY2xpbmljYWxEYXRhJFVsYyksXSwgYWVzKHg9VWxjLCB5PVNpZ25hdHVyZSkpKwogIGdlb21fdmlvbGluKCkrCiAgZ2VvbV9qaXR0ZXIoaGVpZ2h0ID0gMCwgd2lkdGggPSAwLjEsIGFscGhhID0gMC41LCBhZXMoKSkrCiAgI3NjYWxlX2ZpbGxfYnJld2VyKHR5cGU9InF1YWwiLCBwYWxldHRlID0gIkRhcmsyIiwgbmFtZSA9ICJDYW1fMTIxIHJpc2sgZ3JvdXAiKSsKICB4bGFiKCJVbGMgKEFWQVNULU0gU2tpbikiKSsKICB5bGFiKCJWU1Qgbm9ybWFsaXplZCBHUkFNRDFCIGV4cHJlc3Npb24gKHN0YW5kLikiKSsKICAjZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gcXVhbnRpbGUoY2xpbmljYWxEYXRhJFNpZ25hdHVyZSwgMC43NSksIGNvbG9yID0gImdyZXkiLCBsaW5ldHlwZSA9ICJsb25nZGFzaCIpKwogIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NywgIGZhbWlseT0ic2FucyIpKSsKICBnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhjb21wYXJpc29ucyA9IG15X2NvbXBhcmlzb25zLCBtZXRob2QgPSAidC50ZXN0Iiwgc2l6ZSA9IDIuNSwgZmFtaWx5PSJzYW5zIikjKyAjIEFkZCBwYWlyd2lzZSBjb21wYXJpc29ucyBwLXZhbHVlCiAgI2dncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA2LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCmBgYAoKI0NoZWNrIGFzc29jaWF0aW9uIG9mIEdSQU1EMUIgd2l0aCBCcmVzCmBgYHtyfQpteV9jb21wYXJpc29ucyA8LSBsaXN0KCBjKCI8PSAyLjAgbW0iLCAiPjItNG1tIiksIGMoIjw9IDIuMCBtbSIsICI+NC4wbW0iKSwgYygiPjItNG1tIiwgIj40LjBtbSIpKQpnZ3Bsb3QoY2xpbmljYWxEYXRhWyFpcy5uYShjbGluaWNhbERhdGEkQnJlcyksXSwgYWVzKHg9QnJlcywgeT1TaWduYXR1cmUpKSsKICBnZW9tX3Zpb2xpbigpKwogIGdlb21faml0dGVyKGhlaWdodCA9IDAsIHdpZHRoID0gMC4xLCBhbHBoYSA9IDAuNSwgYWVzKCkpKwogICNzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJxdWFsIiwgcGFsZXR0ZSA9ICJEYXJrMiIsIG5hbWUgPSAiQ2FtXzEyMSByaXNrIGdyb3VwIikrCiAgeGxhYigiQnJlcyAoQVZBU1QtTSBTa2luKSIpKwogIHlsYWIoIlZTVCBub3JtYWxpemVkIEdSQU1EMUIgZXhwcmVzc2lvbiAoc3RhbmQuKSIpKwogICNnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBxdWFudGlsZShjbGluaWNhbERhdGEkU2lnbmF0dXJlLCAwLjc1KSwgY29sb3IgPSAiZ3JleSIsIGxpbmV0eXBlID0gImxvbmdkYXNoIikrCiAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT03LCAgZmFtaWx5PSJzYW5zIikpKwogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zID0gbXlfY29tcGFyaXNvbnMsIG1ldGhvZCA9ICJ0LnRlc3QiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSsgIyBBZGQgcGFpcndpc2UgY29tcGFyaXNvbnMgcC12YWx1ZQogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA2LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCmBgYAoKI0NoZWNrIGFzc29jaWF0aW9uIG9mIEdSQU1EMUIgd2l0aCBTdGFnZQpgYGB7cn0KbXlfY29tcGFyaXNvbnMgPC0gbGlzdCggYygiSUlCIiwgIklJQyIpLCBjKCJJSUIiLCAiSUlJQSIpLCBjKCJJSUIiLCAiSUlJQiIpLCBjKCJJSUIiLCAiSUlJQyIpLAogICAgICAgICAgICAgICAgICAgICAgICBjKCJJSUMiLCAiSUlJQSIpLCBjKCJJSUMiLCAiSUlJQiIpLCBjKCJJSUMiLCAiSUlJQyIpLAogICAgICAgICAgICAgICAgICAgICAgICBjKCJJSUlBIiwgIklJSUIiKSwgYygiSUlJQSIsICJJSUlDIiksCiAgICAgICAgICAgICAgICAgICAgICAgIGMoIklJSUIiLCAiSUlJQyIpKQpnZ3Bsb3QoY2xpbmljYWxEYXRhWyFpcy5uYShjbGluaWNhbERhdGEkU3RhZ2UpLF0sIGFlcyh4PVN0YWdlLCB5PVNpZ25hdHVyZSkpKwogIGdlb21fdmlvbGluKCkrCiAgZ2VvbV9qaXR0ZXIoaGVpZ2h0ID0gMCwgd2lkdGggPSAwLjEsIGFscGhhID0gMC41LCBhZXMoKSkrCiAgI3NjYWxlX2ZpbGxfYnJld2VyKHR5cGU9InF1YWwiLCBwYWxldHRlID0gIkRhcmsyIiwgbmFtZSA9ICJDYW1fMTIxIHJpc2sgZ3JvdXAiKSsKICB4bGFiKCJTdGFnZSAoQVZBU1QtTSBTa2luKSIpKwogIHlsYWIoIlZTVCBub3JtYWxpemVkIEdSQU1EMUIgZXhwcmVzc2lvbiAoc3RhbmQuKSIpKwogICNnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBxdWFudGlsZShjbGluaWNhbERhdGEkU2lnbmF0dXJlLCAwLjc1KSwgY29sb3IgPSAiZ3JleSIsIGxpbmV0eXBlID0gImxvbmdkYXNoIikrCiAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT03LCAgZmFtaWx5PSJzYW5zIikpKwogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zID0gbXlfY29tcGFyaXNvbnMsIG1ldGhvZCA9ICJ0LnRlc3QiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSsgIyBBZGQgcGFpcndpc2UgY29tcGFyaXNvbnMgcC12YWx1ZQogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA5LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCmBgYApgYGB7cn0KZ2dwbG90KGNsaW5pY2FsRGF0YVshaXMubmEoY2xpbmljYWxEYXRhJFN0YWdlX2JpbmFyeSksXSwgYWVzKHg9U3RhZ2VfYmluYXJ5LCB5PVNpZ25hdHVyZSkpKwogIGdlb21fdmlvbGluKCkrCiAgZ2VvbV9qaXR0ZXIoaGVpZ2h0ID0gMCwgd2lkdGggPSAwLjEsIGFscGhhID0gMC41LCBhZXMoKSkrCiAgI3NjYWxlX2ZpbGxfYnJld2VyKHR5cGU9InF1YWwiLCBwYWxldHRlID0gIkRhcmsyIiwgbmFtZSA9ICJDYW1fMTIxIHJpc2sgZ3JvdXAiKSsKICB4bGFiKCJTdGFnZSAoQVZBU1QtTSBTa2luKSIpKwogIHlsYWIoIlZTVCBub3JtYWxpemVkIEdSQU1EMUIgZXhwcmVzc2lvbiAoc3RhbmQuKSIpKwogICNnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBxdWFudGlsZShjbGluaWNhbERhdGEkU2lnbmF0dXJlLCAwLjc1KSwgY29sb3IgPSAiZ3JleSIsIGxpbmV0eXBlID0gImxvbmdkYXNoIikrCiAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT03LCAgZmFtaWx5PSJzYW5zIikpKwogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKG1ldGhvZCA9ICJ0LnRlc3QiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrICMgQWRkIHBhaXJ3aXNlIGNvbXBhcmlzb25zIHAtdmFsdWUKICAjZ2dwdWJyOjpzdGF0X2NvbXBhcmVfbWVhbnMobGFiZWwueSA9IDksIG1ldGhvZCA9ICJhbm92YSIsIHNpemUgPSAyLjUsIGZhbWlseT0ic2FucyIpCmBgYAoKI0NoZWNrIGFzc29jaWF0aW9uIG9mIEdSQU1EMUIgd2l0aCBTaXRlCmBgYHtyfQpteV9jb21wYXJpc29ucyA8LSBsaXN0KCBjKCJIZWFkIGFuZCBuZWNrIiwgIkxvd2VyIGxpbXMiKSwgYygiSGVhZCBhbmQgbmVjayIsICJUcnVuayIpLCBjKCJIZWFkIGFuZCBuZWNrIiwgIlVwcGVyIGxpbWJzIiksCiAgICAgICAgICAgICAgICAgICAgICAgIGMoIkxvd2VyIGxpbXMiLCAiVHJ1bmsiKSwgYygiTG93ZXIgbGltcyIsICJVcHBlciBsaW1icyIpLAogICAgICAgICAgICAgICAgICAgICAgICBjKCJUcnVuayIsICJVcHBlciBsaW1icyIpKQpnZ3Bsb3QoY2xpbmljYWxEYXRhWyFpcy5uYShjbGluaWNhbERhdGEkU2l0ZSksXSwgYWVzKHg9U2l0ZSwgeT1TaWduYXR1cmUpKSsKICBnZW9tX3Zpb2xpbigpKwogIGdlb21faml0dGVyKGhlaWdodCA9IDAsIHdpZHRoID0gMC4xLCBhbHBoYSA9IDAuNSwgYWVzKCkpKwogICNzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJxdWFsIiwgcGFsZXR0ZSA9ICJEYXJrMiIsIG5hbWUgPSAiQ2FtXzEyMSByaXNrIGdyb3VwIikrCiAgeGxhYigiU2l0ZSAoQVZBU1QtTSBTa2luKSIpKwogIHlsYWIoIlZTVCBub3JtYWxpemVkIEdSQU1EMUIgZXhwcmVzc2lvbiAoc3RhbmQuKSIpKwogICNnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBxdWFudGlsZShjbGluaWNhbERhdGEkU2lnbmF0dXJlLCAwLjc1KSwgY29sb3IgPSAiZ3JleSIsIGxpbmV0eXBlID0gImxvbmdkYXNoIikrCiAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT03LCAgZmFtaWx5PSJzYW5zIikpKwogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zID0gbXlfY29tcGFyaXNvbnMsIG1ldGhvZCA9ICJ0LnRlc3QiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSsgIyBBZGQgcGFpcndpc2UgY29tcGFyaXNvbnMgcC12YWx1ZQogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA5LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCgpgYGAKCiNDaGVjayBhc3NvY2lhdGlvbiBvZiBHUkFNRDFCIHdpdGggQlJBRiBtdXRhdGlvbgpgYGB7cn0KbXlfY29tcGFyaXNvbnMgPC0gbGlzdCggYygiVjYwMEUiLCAiV1QiKSkKZ2dwbG90KGNsaW5pY2FsRGF0YVshaXMubmEoY2xpbmljYWxEYXRhJEJSQUYpLF0sIGFlcyh4PUJSQUYsIHk9U2lnbmF0dXJlKSkrCiAgZ2VvbV92aW9saW4oKSsKICBnZW9tX2ppdHRlcihoZWlnaHQgPSAwLCB3aWR0aCA9IDAuMSwgYWxwaGEgPSAwLjUsIGFlcygpKSsKICAjc2NhbGVfZmlsbF9icmV3ZXIodHlwZT0icXVhbCIsIHBhbGV0dGUgPSAiRGFyazIiLCBuYW1lID0gIkNhbV8xMjEgcmlzayBncm91cCIpKwogIHhsYWIoIkJSQUYgKEFWQVNULU0gU2tpbikiKSsKICB5bGFiKCJWU1Qgbm9ybWFsaXplZCBHUkFNRDFCIGV4cHJlc3Npb24gKHN0YW5kLikiKSsKICAjZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gcXVhbnRpbGUoY2xpbmljYWxEYXRhJFNpZ25hdHVyZSwgMC43NSksIGNvbG9yID0gImdyZXkiLCBsaW5ldHlwZSA9ICJsb25nZGFzaCIpKwogIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NywgIGZhbWlseT0ic2FucyIpKSsKICBnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhjb21wYXJpc29ucyA9IG15X2NvbXBhcmlzb25zLCBtZXRob2QgPSAidC50ZXN0Iiwgc2l6ZSA9IDIuNSwgZmFtaWx5PSJzYW5zIikjKyAjIEFkZCBwYWlyd2lzZSBjb21wYXJpc29ucyBwLXZhbHVlCiAgI2dncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA5LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCmBgYAoKI0NoZWNrIGFzc29jaWF0aW9uIG9mIEdSQU1EMUIgd2l0aCBOUkFTIG11dGF0aW9uCmBgYHtyfQpteV9jb21wYXJpc29ucyA8LSBsaXN0KCBjKCJNdXRhbnQiLCAiV1QiKSkKZ2dwbG90KGNsaW5pY2FsRGF0YVshaXMubmEoY2xpbmljYWxEYXRhJE5SQVMpLF0sIGFlcyh4PU5SQVMsIHk9U2lnbmF0dXJlKSkrCiAgZ2VvbV92aW9saW4oKSsKICBnZW9tX2ppdHRlcihoZWlnaHQgPSAwLCB3aWR0aCA9IDAuMSwgYWxwaGEgPSAwLjUsIGFlcygpKSsKICAjc2NhbGVfZmlsbF9icmV3ZXIodHlwZT0icXVhbCIsIHBhbGV0dGUgPSAiRGFyazIiLCBuYW1lID0gIkNhbV8xMjEgcmlzayBncm91cCIpKwogIHhsYWIoIk5SQVMgKEFWQVNULU0gU2tpbikiKSsKICB5bGFiKCJWU1Qgbm9ybWFsaXplZCBHUkFNRDFCIGV4cHJlc3Npb24gKHN0YW5kLikiKSsKICAjZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gcXVhbnRpbGUoY2xpbmljYWxEYXRhJFNpZ25hdHVyZSwgMC43NSksIGNvbG9yID0gImdyZXkiLCBsaW5ldHlwZSA9ICJsb25nZGFzaCIpKwogIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NywgIGZhbWlseT0ic2FucyIpKSsKICBnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhjb21wYXJpc29ucyA9IG15X2NvbXBhcmlzb25zLCBtZXRob2QgPSAidC50ZXN0Iiwgc2l6ZSA9IDIuNSwgZmFtaWx5PSJzYW5zIikjKyAjIEFkZCBwYWlyd2lzZSBjb21wYXJpc29ucyBwLXZhbHVlCiAgI2dncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA5LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCmBgYAoKI0NoZWNrIGFzc29jaWF0aW9uIG9mIEdSQU1EMUIgd2l0aCBUSUwgY291bnRzCmBgYHtyfQpqdF9uc19zbV9zY29yZXMgPSByZWFkLnRhYmxlKCIuLi8uLi8uLi9KVF9OU19TTV9Db21iaW5lZC54bHMgLSBKVF9OU19TTS50c3YiLCBzZXAgPSAiXHQiLCBoZWFkZXIgPSBUUlVFLCBxdW90ZSA9ICIiKQpjb21iaW5lZFNjb3JlIDwtIGRhdGEuZnJhbWUoIlNjYW5uZWRfZmlsZS5tZSI9IGp0X25zX3NtX3Njb3JlcyRKVF9TY2FubmVkX2ZpbGUubWUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSLlNlcV9zYW1wbGVJRCI9IGp0X25zX3NtX3Njb3JlcyRKVF9SLlNlcV9zYW1wbGVJRCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNsYXJrU2NvcmUiPSBpZmVsc2UoanRfbnNfc21fc2NvcmVzJEpUX0NsYXJrX3Njb3JlPT1qdF9uc19zbV9zY29yZXMkTlNfQ2xhcmtfc2NvcmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBqdF9uc19zbV9zY29yZXMkSlRfQ2xhcmtfc2NvcmUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAganRfbnNfc21fc2NvcmVzJFNNX0NsYXJrX3Njb3JlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1JQVNjb3JlIj0gaWZlbHNlKGp0X25zX3NtX3Njb3JlcyRKVF9USUxfZ3JhZGU9PWp0X25zX3NtX3Njb3JlcyROU19USUwuR1JBREUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBqdF9uc19zbV9zY29yZXMkSlRfVElMX2dyYWRlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGp0X25zX3NtX3Njb3JlcyRTTV9USUxfZ3JhZGUpKQojUmVtb3ZlIGR1cGxpY2F0ZWQgZW50cmllcwpjb21iaW5lZFNjb3JlPC1jb21iaW5lZFNjb3JlWyFkdXBsaWNhdGVkKGNvbWJpbmVkU2NvcmUkUi5TZXFfc2FtcGxlSUQpLCBdCnJvd25hbWVzKGNvbWJpbmVkU2NvcmUpPC1jb21iaW5lZFNjb3JlJFIuU2VxX3NhbXBsZUlEICNyZW5hbWUgcm93bmFtZXMgd2l0aCBzYW1wbGUgaWRzCmBgYAoKYGBge3J9CnJvd25hbWVzKGNsaW5pY2FsRGF0YSk8LWNsaW5pY2FsRGF0YSRSTkEuU2VxLlNhbXBsZQpkZjwtZGF0YS5mcmFtZSgiRXZlbnRNZXQiPWNsaW5pY2FsRGF0YSRFdmVudE1ldCwKICAgICAgICAgICAgICAgIkNsYXJrU2NvcmUiPWNvbWJpbmVkU2NvcmVbcm93bmFtZXMoY2xpbmljYWxEYXRhKSwgIkNsYXJrU2NvcmUiXSwKICAgICAgICAgICAgICAgIk1JQVNjb3JlIj1jb21iaW5lZFNjb3JlW3Jvd25hbWVzKGNsaW5pY2FsRGF0YSksICJNSUFTY29yZSJdLAogICAgICAgICAgICAgICAiU2lnbmF0dXJlIj1jbGluaWNhbERhdGEkU2lnbmF0dXJlKQpyb3duYW1lcyhkZik8LXJvd25hbWVzKGNsaW5pY2FsRGF0YSkKCmRmJENsYXJrU2NvcmUgPC0gYXMuZmFjdG9yKGRmJENsYXJrU2NvcmUpCmxldmVscyhkZiRDbGFya1Njb3JlKSA8LSBjKCJhYnNlbnQiLCAibm9uYnJpc2siLCAiYnJpc2siKQpgYGAKCmBgYHtyfQpteV9jb21wYXJpc29ucyA8LSBsaXN0KCBjKCJhYnNlbnQiLCAibm9uYnJpc2siKSwgYygibm9uYnJpc2siLCAiYnJpc2siKSwgYygiYWJzZW50IiwgImJyaXNrIikgKQojZGYkSlRfQ2xhcmtfc2NvcmUgPC0gZmFjdG9yKGRmJEpUX0NsYXJrX3Njb3JlLCBsZXZlbHMgPSBjKCJhYnNlbnQiLCAibm9uYnJpc2siLCAiYnJpc2siKSkKZ2dwbG90KGRmWyFpcy5uYShkZiRDbGFya1Njb3JlKSwgXSwgYWVzKHg9Q2xhcmtTY29yZSwgeT1TaWduYXR1cmUjLCBjb2xvcj1EUkJyYWluCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApKSsKICBnZW9tX3Zpb2xpbigpKwogIGdlb21faml0dGVyKGhlaWdodCA9IDAsIHdpZHRoID0gMC4xLCBhbHBoYSA9IDAuNSwgYWVzKCkpKwogICNzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJxdWFsIiwgcGFsZXR0ZSA9ICJEYXJrMiIsIG5hbWUgPSAiQ2FtXzEyMSByaXNrIGdyb3VwIikrCiAgeGxhYigiQ2xhcmsgc2NvcmUgKEFWQVNULU0gU2tpbikiKSsKICB5bGFiKCJWU1Qgbm9ybWFsaXplZCBHUkFNRDFCIGV4cHJlc3Npb24gKHN0YW5kLikiKSsKICAjZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gcXVhbnRpbGUoZGYkU2lnbmF0dXJlLCAwLjc1KSwgY29sb3IgPSAiZ3JleSIsIGxpbmV0eXBlID0gImxvbmdkYXNoIikrCiAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT03LCAgZmFtaWx5PSJzYW5zIikpKwogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zID0gbXlfY29tcGFyaXNvbnMsIG1ldGhvZCA9ICJ0LnRlc3QiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSsgIyBBZGQgcGFpcndpc2UgY29tcGFyaXNvbnMgcC12YWx1ZQogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA2LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCiAjIGZhY2V0X3dyYXAofkRSQnJhaW4pIyBBZGQgZ2xvYmFsIHAtdmFsdWUKYGBgCgpgYGB7cn0KbXlfY29tcGFyaXNvbnMgPC0gbGlzdCggYygiMCIsICIxIiksIGMoIjAiLCAiMiIpLCBjKCIwIiwgIjMiKSwgYygiMSIsICIyIiksIGMoIjEiLCAiMyIpLCBjKCIyIiwgIjMiKSkKZGYkTUlBU2NvcmUgPC0gYXMuZmFjdG9yKGRmJE1JQVNjb3JlKQpnZ3Bsb3QoZGZbIWlzLm5hKGRmJE1JQVNjb3JlKSwgXSwgYWVzKHg9TUlBU2NvcmUsIHk9U2lnbmF0dXJlIywgY29sb3I9RFJCcmFpbgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkpKwogIGdlb21fdmlvbGluKCkrCiAgZ2VvbV9qaXR0ZXIoaGVpZ2h0ID0gMCwgd2lkdGggPSAwLjEsIGFscGhhID0gMC41KSsKICBzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJxdWFsIiwgcGFsZXR0ZSA9ICJEYXJrMiIsIG5hbWUgPSAiTUlBIHNjb3JlIikrCiAgeGxhYigiTUlBIHNjb3JlIChBVkFTVC1NIFNraW4pIikrCiAgeWxhYigiVlNUIG5vcm1hbGl6ZWQgR1JBTUQxQiBleHByZXNzaW9uIChzdGFuZC4pIikrCiAgI2dlb21faGxpbmUoeWludGVyY2VwdCA9IHF1YW50aWxlKGRmJFNpZ25hdHVyZSwgMC43NSksIGNvbG9yID0gImdyZXkiLCBsaW5ldHlwZSA9ICJsb25nZGFzaCIpKwogIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NywgIGZhbWlseT0ic2FucyIpKSsKICBnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhjb21wYXJpc29ucyA9IG15X2NvbXBhcmlzb25zLCBtZXRob2QgPSAidC50ZXN0Iiwgc2l6ZSA9IDIuNSwgZmFtaWx5PSJzYW5zIikrICMgQWRkIHBhaXJ3aXNlIGNvbXBhcmlzb25zIHAtdmFsdWUKICBnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhsYWJlbC55ID0gNiwgbWV0aG9kID0gImFub3ZhIiwgc2l6ZSA9IDIuNSwgZmFtaWx5PSJzYW5zIikjKwogICNmYWNldF93cmFwKH5EUkJyYWluKQogICAgICMgQWRkIGdsb2JhbCBwLXZhbHVlCgpgYGAKYGBge3J9CiNteV9jb21wYXJpc29ucyA8LSBsaXN0KCBjKCJNdXRhbnQiLCAiV1QiKSkKZ2dwbG90KGNsaW5pY2FsRGF0YVshaXMubmEoY2xpbmljYWxEYXRhJHRyZWF0bWVudCksXSwgYWVzKHg9dHJlYXRtZW50LCB5PVNpZ25hdHVyZSkpKwogIGdlb21fdmlvbGluKCkrCiAgZ2VvbV9qaXR0ZXIoaGVpZ2h0ID0gMCwgd2lkdGggPSAwLjEsIGFscGhhID0gMC41LCBhZXMoKSkrCiAgI3NjYWxlX2ZpbGxfYnJld2VyKHR5cGU9InF1YWwiLCBwYWxldHRlID0gIkRhcmsyIiwgbmFtZSA9ICJDYW1fMTIxIHJpc2sgZ3JvdXAiKSsKICB4bGFiKCJUcmVhdG1lbnQgKEFWQVNULU0gU2tpbikiKSsKICB5bGFiKCJWU1Qgbm9ybWFsaXplZCBHUkFNRDFCIGV4cHJlc3Npb24gKHN0YW5kLikiKSsKICAjZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gcXVhbnRpbGUoY2xpbmljYWxEYXRhJFNpZ25hdHVyZSwgMC43NSksIGNvbG9yID0gImdyZXkiLCBsaW5ldHlwZSA9ICJsb25nZGFzaCIpKwogIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NywgIGZhbWlseT0ic2FucyIpKSsKICBnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSAidC50ZXN0Iiwgc2l6ZSA9IDIuNSwgZmFtaWx5PSJzYW5zIikjKyAjIEFkZCBwYWlyd2lzZSBjb21wYXJpc29ucyBwLXZhbHVlCiAgI2dncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA5LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCmBgYApgYGB7cn0KI215X2NvbXBhcmlzb25zIDwtIGxpc3QoIGMoIk11dGFudCIsICJXVCIpKQpnZ3Bsb3QoY2xpbmljYWxEYXRhWyFpcy5uYShjbGluaWNhbERhdGEkRUNPRyksXSwgYWVzKHg9RUNPRywgeT1TaWduYXR1cmUpKSsKICBnZW9tX3Zpb2xpbigpKwogIGdlb21faml0dGVyKGhlaWdodCA9IDAsIHdpZHRoID0gMC4xLCBhbHBoYSA9IDAuNSwgYWVzKCkpKwogICNzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJxdWFsIiwgcGFsZXR0ZSA9ICJEYXJrMiIsIG5hbWUgPSAiQ2FtXzEyMSByaXNrIGdyb3VwIikrCiAgeGxhYigiRUNPRyAoQVZBU1QtTSBTa2luKSIpKwogIHlsYWIoIlZTVCBub3JtYWxpemVkIEdSQU1EMUIgZXhwcmVzc2lvbiAoc3RhbmQuKSIpKwogICNnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBxdWFudGlsZShjbGluaWNhbERhdGEkU2lnbmF0dXJlLCAwLjc1KSwgY29sb3IgPSAiZ3JleSIsIGxpbmV0eXBlID0gImxvbmdkYXNoIikrCiAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT03LCAgZmFtaWx5PSJzYW5zIikpKwogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKG1ldGhvZCA9ICJ0LnRlc3QiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrICMgQWRkIHBhaXJ3aXNlIGNvbXBhcmlzb25zIHAtdmFsdWUKICAjZ2dwdWJyOjpzdGF0X2NvbXBhcmVfbWVhbnMobGFiZWwueSA9IDksIG1ldGhvZCA9ICJhbm92YSIsIHNpemUgPSAyLjUsIGZhbWlseT0ic2FucyIpIysKYGBgCgojUGVyZm9ybSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcwpgYGB7cn0KY29tYmluZWRTY29yZSRDbGFya1Njb3JlIDwtIGFzLmZhY3Rvcihjb21iaW5lZFNjb3JlJENsYXJrU2NvcmUpCmxldmVscyhjb21iaW5lZFNjb3JlJENsYXJrU2NvcmUpIDwtIGMoImFic2VudCIsICJub25icmlzayIsICJicmlzayIpCmNsaW5pY2FsRGF0YSRDbGFya1Njb3JlPC1jb21iaW5lZFNjb3JlW3Jvd25hbWVzKGNsaW5pY2FsRGF0YSksICJDbGFya1Njb3JlIl0KI21lcmdlKGNsaW5pY2FsRGF0YSwgY29tYmluZWRTY29yZSwgYnkueD0iUk5BLlNlcS5TYW1wbGUiLCBieS55PSJSLlNlcV9zYW1wbGVJRCIpCiNyb3duYW1lcyhjbGluaWNhbERhdGEpPC1jbGluaWNhbERhdGEkUk5BLlNlcS5TYW1wbGUKI2NsaW5pY2FsRGF0YSRDbGFya1Njb3JlPWNvbWJpbmVkU2NvcmUkQ2xhcmtTY29yZVtyb3duYW1lcyhjbGluaWNhbERhdGEpXQpgYGAKCmBgYHtyfQojV2hpbGUgYWNjb3VudGluZyBmb3IgU3RhZ2UsIE5SQVMgbXV0YXRpb24sIFRJTCBzY29yZSBhbmQgRXZlbnRNZXQKI1N1YnNldCB0byBvbmx5IHRob3NlIHNhbXBsZXMgd2hpY2ggaGF2ZSBub24tbWlzc2luZyBlbnRpcmVzIGZvciB0aGVzZSBjb3ZhcmlhdGVzCmNsaW5pY2FsRGF0YV9zdWI8LWNsaW5pY2FsRGF0YVsoIWlzLm5hKGNsaW5pY2FsRGF0YSRTdGFnZSkpJighaXMubmEoY2xpbmljYWxEYXRhJE5SQVMpKSYoIWlzLm5hKGNsaW5pY2FsRGF0YSRDbGFya1Njb3JlKSkmKCFpcy5uYShjbGluaWNhbERhdGEkRXZlbnRNZXQpKSwgXQpkYXRhLmZfc3ViPC1kYXRhLmZbLCByb3duYW1lcyhjbGluaWNhbERhdGFfc3ViKV0KYGBgCgpgYGB7cn0KdGFibGUoY2xpbmljYWxEYXRhX3N1YiRTaWduYXR1cmVHcm91cE1lYW4pCmBgYAoKYGBge3J9CmRkczEgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBkYXRhLmZfc3ViLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSAgID0gY2xpbmljYWxEYXRhX3N1YiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiAgICA9IH4gU3RhZ2UrTlJBUytDbGFya1Njb3JlK0V2ZW50TWV0K1NpZ25hdHVyZUdyb3VwTWVhbikKYGBgCgpgYGB7cn0KdG90YWxjb3VudHMxLmdlbmUgPSByb3dTdW1zKGNvdW50cyhkZHMxKSkKZGRzMSA8LSBkZHMxW3RvdGFsY291bnRzMS5nZW5lPj0xMCxdCmBgYAoKYGBge3J9CiMgZGltZW5zaW9ucyBwb3N0IGZpbHRlcmluZwptID0gbnJvdyhkZHMxKQpuID0gbmNvbChkZHMxKQpgYGAKCmBgYHtyfQpkZHMxIDwtIERFU2VxKGRkczEpCnJlczEgPC0gcmVzdWx0cyhkZHMxKQpyZXMxCmBgYApgYGB7cn0KI01lcmdlIHdpdGggZ2VuZSBuYW1lcyBhbmQgY2hlY2sgaWYgdGhlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBtYWtlIHNlbnNlCnJlczwtZGF0YS5mcmFtZShyZXMxKQpyZXM8LXJlc1tvcmRlcihyZXMkcGFkaiksIF0KcmVzJE5hbWU8LXJlcy5hbm5vdFtyb3duYW1lcyhyZXMpLCAiTmFtZSJdCmhlYWQocmVzKQojR1JBTUQxQiBpcyBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgaW4gR1JBTUQxQiBncm91cHMgYW5kIGlzIGRvd25yZWd1bGF0ZWQgaW4gdGhlIGxvdyBleHByZXNzaW9uIGdyb3VwIGNvbXBhcmVkIHRvIHRoZSBoaWdoLWV4cHJlc3Npb24gZ3JvdXAgc28gYXRsZWFzdCB0aGlzIG1ha2VzIHNlbnNlLgpgYGAKI1BlcmZvcm0gbGZjIHNocmlua2FnZSBmb3IgR1NFQQpgYGB7cn0KbGlicmFyeSgiYXBlZ2xtIikKY29lZk5hbWUgPSByZXN1bHRzTmFtZXMoZGRzMSkKcmVzV2l0aExmY1Nocmlua2FnZSA9IGRhdGEuZnJhbWUobGZjU2hyaW5rKGRkczEsIGNvZWYgPSB0YWlsKGNvZWZOYW1lLCBuPTEpLCB0eXBlPSJhcGVnbG0iKSkKI0FkZCBzdWZmaXggdG8gY29sbmFtZXMgZm9yIGVhc3kgaWRlbnRpZmljYXRpb24gbGF0ZXIgb24uCmNvbG5hbWVzKHJlc1dpdGhMZmNTaHJpbmthZ2UpID0gcGFzdGUoY29sbmFtZXMocmVzV2l0aExmY1Nocmlua2FnZSksImxmY1Nocmlua0FwcGxpZWQiLCBzZXAgPSAiXyIpCiNBZGQgRU5TRU1CTCBJRCBhcyBhIGNvbHVtbiBpbiB0aGUgZGF0YSBmcmFtZSB0byBlYXNlIG1lcmdpbmcgbGF0ZXIKcmVzV2l0aExmY1Nocmlua2FnZSRpZCA9IHJvd25hbWVzKHJlc1dpdGhMZmNTaHJpbmthZ2UpCmBgYAoKYGBge3J9CiNNZXJnZSB0aGVzZSB3aXRoIERFIG9yaWdpbmFsIERFIHJlc3VsdHMKcmVzJGlkPC1yb3duYW1lcyhyZXMpCmRlUmVzdWx0c1VwZGF0ZWQgPSBtZXJnZSh4ID0gcmVzLCB5ID0gZGF0YS5mcmFtZShyZXNXaXRoTGZjU2hyaW5rYWdlKSwgYnkgPSAiaWQiKQpkZVJlc3VsdHNVcGRhdGVkID0gZGVSZXN1bHRzVXBkYXRlZFtvcmRlcihkZVJlc3VsdHNVcGRhdGVkJHBhZGopLF0KI1NhdmUgdGhlIHJlc3VsdHMKd3JpdGUudGFibGUoZGVSZXN1bHRzVXBkYXRlZCwgZmlsZSA9ICIuLi9yZXN1bHRzL2RlX2dzZWEvZGVXaXRoTGZjU2tyaW5rYWdlQXBwbGllZF9HUkFNRDFCX2FsbC50c3YiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBGQUxTRSwgcXVvdGUgPSBGQUxTRSwgc2VwID0gJ1x0JykKI1NhdmUgdGhlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyAocGFkajwwLjEpCndyaXRlLnRhYmxlKGRlUmVzdWx0c1VwZGF0ZWRbZGVSZXN1bHRzVXBkYXRlZCRwYWRqX2xmY1Nocmlua0FwcGxpZWQ8MC4xLCBdLCBmaWxlID0gIi4uL3Jlc3VsdHMvZGVfZ3NlYS9kZVdpdGhMZmNTa3JpbmthZ2VBcHBsaWVkX0dSQU1EMUJfcGFkal8wXzEudHN2IiwgY29sLm5hbWVzID0gVFJVRSwgcm93Lm5hbWVzID0gRkFMU0UsIHF1b3RlID0gRkFMU0UsIHNlcCA9ICdcdCcpCmBgYAoKYGBge3J9CmhlYWQoZGVSZXN1bHRzVXBkYXRlZCkKYGBgCgojQ3JlYXRlIGEgcmFua2VkIGxpc3QgZm9yIEdTRUEKYGBge3J9CiNDcmVhdGUgcmFua2VkIGxpc3QgZm9yIHJ1bm5pbmcgUHJlLXJhbmtlZCBHU0VBIHRvb2wKZGVSZXN1bHRzVXBkYXRlZDwtcmVhZC50YWJsZSgiLi4vcmVzdWx0cy9kZV9nc2VhL2RlV2l0aExmY1Nrcmlua2FnZUFwcGxpZWRfR1JBTUQxQl9hbGwudHN2IiwgaGVhZGVyID0gVCwgc2VwID0gIlx0IiwgcXVvdGUgPSAiIikKcmFua2VkTGlzdCA9IG5hLm9taXQoZGF0YS5mcmFtZShkZVJlc3VsdHNVcGRhdGVkJE5hbWUsIGRlUmVzdWx0c1VwZGF0ZWQkbG9nMkZvbGRDaGFuZ2VfbGZjU2hyaW5rQXBwbGllZCkpCnJhbmtlZExpc3QgPSByYW5rZWRMaXN0W3dpdGgocmFua2VkTGlzdCwgb3JkZXIoZGVSZXN1bHRzVXBkYXRlZC5sb2cyRm9sZENoYW5nZV9sZmNTaHJpbmtBcHBsaWVkLCBkZWNyZWFzaW5nID0gVCkpLCBdCiNTYXZlIHRoZSByYW5rZWQgbGlzdCB0byBhIG5ldyBmaWxlIGZvciBHU0VBLgp3cml0ZS50YWJsZShyYW5rZWRMaXN0LCBmaWxlID0gIi4uL3Jlc3VsdHMvZGVfZ3NlYS9yYW5rZWRMaXN0X0dSQU1EMUIucm5rIiwgY29sLm5hbWVzID0gRkFMU0UsIHJvdy5uYW1lcyA9IEZBTFNFLCBxdW90ZSA9IEZBTFNFLCBzZXAgPSAnXHQnKQpgYGAKCiNQbG90IHN1cnZpdmFsIGN1cnZlcyBiZXR3ZWVuIHRoZSBjb250aW51b3VzIHNpZ25hdHVyZSBhbmQgdGhlIHR3byBncm91cHMKIyMgRmlyc3QgYWRkIG5lY2Vzc3NhcnkgY29sdW1ucwpgYGB7cn0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoInN1cnZpdmFsIikpCiMgc3Vydml2YWwgb3V0Y29tZSAxOiAiZCIgZm9yIGRlYXRoIGFuZCAibHRyYyIgZm9yIGxlZnQgdHJ1bmNhdGVkIGFuZCByaWdodCBjZW5zb3JlZApjbGluaWNhbERhdGEkc3Vydml2YWxfZF9sdHJjID0gU3Vydih0aW1lICA9IGFzLm51bWVyaWMoZGlmZnRpbWUoY2xpbmljYWxEYXRhJERPRSwgY2xpbmljYWxEYXRhJEREaWFnKSkvMzY1LjI1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lMiA9IGFzLm51bWVyaWMoZGlmZnRpbWUoY2xpbmljYWxEYXRhJERPQywgY2xpbmljYWxEYXRhJEREaWFnKSkvMzY1LjI1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBldmVudCA9IGlmZWxzZShjbGluaWNhbERhdGEkRGVhZCA9PSBGQUxTRSwgMCwgMSkpCgojIHN1cnZpdmFsIG91dGNvbWUgMjogInJkIiBmb3IgcmVsYXBzZSBvciBkZWF0aCwgImx0cmMiIGZvciBsZWZ0IHRydW5jYXRlZCBhbmQgcmlnaHQgY2Vuc29yZWQKY2xpbmljYWxEYXRhJHN1cnZpdmFsX3JkX2x0cmMgPSBTdXJ2KHRpbWUgID0gYXMubnVtZXJpYyhkaWZmdGltZShjbGluaWNhbERhdGEkRE9FLCBjbGluaWNhbERhdGEkRERpYWcpKS8zNjUuMjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lMiA9IGFzLm51bWVyaWMoZGlmZnRpbWUoYXBwbHkoY2xpbmljYWxEYXRhWywgYygiRE9DIiwgIkREaXN0TWV0cyIpXSwxLG1pbixuYS5ybT1UUlVFKSwgY2xpbmljYWxEYXRhJEREaWFnKSkvMzY1LjI1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXZlbnQgPSBpZmVsc2UoKGNsaW5pY2FsRGF0YSREZWFkID09IEZBTFNFICYgY2xpbmljYWxEYXRhJEV2ZW50TWV0ID09ICJObyIpLCAwLCAxKSkKCmlmKHRpc3N1ZT09IlNraW4iKXsKICBqdF9uc19zbV9zY29yZXMgPSByZWFkLnRhYmxlKCIuLi9KVF9OU19TTV9Db21iaW5lZC54bHMgLSBKVF9OU19TTS50c3YiLCBzZXAgPSAiXHQiLCBoZWFkZXIgPSBUUlVFLCBxdW90ZSA9ICIiKQogIGNvbWJpbmVkU2NvcmUgPC0gZGF0YS5mcmFtZSgiU2Nhbm5lZF9maWxlLm1lIj0ganRfbnNfc21fc2NvcmVzJEpUX1NjYW5uZWRfZmlsZS5tZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSLlNlcV9zYW1wbGVJRCI9IGp0X25zX3NtX3Njb3JlcyRKVF9SLlNlcV9zYW1wbGVJRCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDbGFya1Njb3JlIj1pZmVsc2UoanRfbnNfc21fc2NvcmVzJEpUX0NsYXJrX3Njb3JlPT1qdF9uc19zbV9zY29yZXMkTlNfQ2xhcmtfc2NvcmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAganRfbnNfc21fc2NvcmVzJEpUX0NsYXJrX3Njb3JlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBqdF9uc19zbV9zY29yZXMkU01fQ2xhcmtfc2NvcmUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiTUlBU2NvcmUiPSBpZmVsc2UoanRfbnNfc21fc2NvcmVzJEpUX1RJTF9ncmFkZT09anRfbnNfc21fc2NvcmVzJE5TX1RJTC5HUkFERSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGp0X25zX3NtX3Njb3JlcyRKVF9USUxfZ3JhZGUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAganRfbnNfc21fc2NvcmVzJFNNX1RJTF9ncmFkZSkpCiAgI1JlbW92ZSBkdXBsaWNhdGVkIGVudHJpZXMKICBjb21iaW5lZFNjb3JlPC1jb21iaW5lZFNjb3JlWyFkdXBsaWNhdGVkKGNvbWJpbmVkU2NvcmUkUi5TZXFfc2FtcGxlSUQpLCBdCiAgcm93bmFtZXMoY29tYmluZWRTY29yZSk8LWNvbWJpbmVkU2NvcmUkUi5TZXFfc2FtcGxlSUQgI3JlbmFtZSByb3duYW1lcyB3aXRoIHNhbXBsZSBpZHMKICBjb21iaW5lZFNjb3JlJENsYXJrU2NvcmUgPC0gYXMuZmFjdG9yKGNvbWJpbmVkU2NvcmUkQ2xhcmtTY29yZSkKICBsZXZlbHMoY29tYmluZWRTY29yZSRDbGFya1Njb3JlKSA8LSBjKCJhYnNlbnQiLCAibm9uYnJpc2siLCAiYnJpc2siKQogIGNsaW5pY2FsRGF0YTwtbWVyZ2UoY2xpbmljYWxEYXRhLCBjb21iaW5lZFNjb3JlLCBieS54PSJSTkEuU2VxLlNhbXBsZSIsIGJ5Lnk9IlIuU2VxX3NhbXBsZUlEIikKICByb3duYW1lcyhjbGluaWNhbERhdGEpPC1jbGluaWNhbERhdGEkUk5BLlNlcS5TYW1wbGUKfQpgYGAKCiMjIHRoZW4gY2FsY3VsYXRlIHRoZSB2YWx1ZXMKYGBge3J9Cm9zPC1kYXRhLmZyYW1lKCkKcGZzPC1kYXRhLmZyYW1lKCkKdGVtcDwtZGF0YS5mcmFtZSgpCgpjb21iaW5hdGlvbnM8LWMoIlNpZ25hdHVyZSIsICJTaWduYXR1cmVHcm91cE1lYW4iLCAiU2lnbmF0dXJlR3JvdXBNZWRpYW4iLCAiU2lnbmF0dXJlR3JvdXAwXzMzIiwgIlNpZ25hdHVyZUdyb3VwMF82NiIsICJTaWduYXR1cmVHcm91cERlbnNpdHlJbnRlcnNlY3Rpb24iKQoKZm9yIChjb21iaW5hdGlvbiBpbiBjb21iaW5hdGlvbnMpIHsKICBpZiAodGlzc3VlPT0iU2tpbiIpIHsKICAgIG9zX2Zvcm11bGE8LXBhc3RlMCgic3Vydml2YWxfZF9sdHJjfiIsY29tYmluYXRpb24sIitTZXgrQWdlK2FzLm51bWVyaWMoTmNsYXNzKSthcy5jaGFyYWN0ZXIoU3RhZ2UpK0VDT0crdHJlYXRtZW50K0NsYXJrU2NvcmUiKQogICAgcGZzX2Zvcm11bGE8LXBhc3RlMCgic3Vydml2YWxfcmRfbHRyY34iLGNvbWJpbmF0aW9uLCIrU2V4K0FnZSthcy5udW1lcmljKE5jbGFzcykrYXMuY2hhcmFjdGVyKFN0YWdlKStFQ09HK3RyZWF0bWVudCtDbGFya1Njb3JlIikKICB9IGVsc2V7CiAgICBvc19mb3JtdWxhPC1wYXN0ZTAoInN1cnZpdmFsX2RfbHRyY34iLGNvbWJpbmF0aW9uLCIrU2V4K0FnZSthcy5udW1lcmljKE5jbGFzcykrYXMuY2hhcmFjdGVyKFN0YWdlKStFQ09HK3RyZWF0bWVudCIpCiAgICBwZnNfZm9ybXVsYTwtcGFzdGUwKCJzdXJ2aXZhbF9yZF9sdHJjfiIsY29tYmluYXRpb24sIitTZXgrQWdlK2FzLm51bWVyaWMoTmNsYXNzKSthcy5jaGFyYWN0ZXIoU3RhZ2UpK0VDT0crdHJlYXRtZW50IikKICB9CiAgCiAgZml0ID0gY29lZihzdW1tYXJ5KGNveHBoKGFzLmZvcm11bGEob3NfZm9ybXVsYSksIGRhdGE9Y2xpbmljYWxEYXRhKSkpCiAgbWlkICA9IGZpdFsxLGMoImV4cChjb2VmKSIpXQogIGxvdyAgPSBleHAoZml0WzEsYygiY29lZiIpXS0KICAgICAgICAgICAgICBxbm9ybSguOTc1KSpmaXRbMSxjKCJzZShjb2VmKSIpXSkgICAgICAgICAgICAgICAgICAgCiAgaGlnaCA9IGV4cChmaXRbMSxjKCJjb2VmIildKwogICAgICAgICAgICAgIHFub3JtKC45NzUpKmZpdFsxLGMoInNlKGNvZWYpIildKSAgCiAgcHZhbCA9IGZpdFsxLGMoIlByKD58enwpIildCiAgdGVtcFsiU2lnbmF0dXJlIixjKCJTaWduYXR1cmUiLCAiSFIiLCJsb3ciLCJoaWdoIiwicHZhbCIpXSA9IGMocm93bmFtZXMoZml0KVsxXSxtaWQsbG93LGhpZ2gscHZhbCkKICBvczwtcmJpbmQob3MsIHRlbXApCiAgCiAgZml0ID0gY29lZihzdW1tYXJ5KGNveHBoKGFzLmZvcm11bGEocGZzX2Zvcm11bGEpLCBkYXRhPWNsaW5pY2FsRGF0YSkpKQogIG1pZCAgPSBmaXRbMSxjKCJleHAoY29lZikiKV0KICBsb3cgID0gZXhwKGZpdFsxLGMoImNvZWYiKV0tCiAgICAgICAgICAgICAgcW5vcm0oLjk3NSkqZml0WzEsYygic2UoY29lZikiKV0pICAgICAgICAgICAgICAgICAgIAogIGhpZ2ggPSBleHAoZml0WzEsYygiY29lZiIpXSsKICAgICAgICAgICAgICBxbm9ybSguOTc1KSpmaXRbMSxjKCJzZShjb2VmKSIpXSkgIAogIHB2YWwgPSBmaXRbMSxjKCJQcig+fHp8KSIpXQogIHRlbXBbIlNpZ25hdHVyZSIsYygiU2lnbmF0dXJlIiwgIkhSIiwibG93IiwiaGlnaCIsInB2YWwiKV0gPSBjKHJvd25hbWVzKGZpdClbMV0sbWlkLGxvdyxoaWdoLHB2YWwpCiAgcGZzPC1yYmluZChwZnMsIHRlbXApCn0KCndyaXRlLnRhYmxlKG9zLCBwYXN0ZSgiLi4vcmVzdWx0cy9zdXJ2aXZhbC9BVkFTVC1NXyIsdGlzc3VlLCJfb3MudHN2Iiwgc2VwPSIiKSwgc2VwPSJcdCIsIGNvbC5uYW1lcyA9IFQsIHJvdy5uYW1lcyA9IEYpCndyaXRlLnRhYmxlKHBmcywgcGFzdGUoIi4uL3Jlc3VsdHMvc3Vydml2YWwvQVZBU1QtTV8iLHRpc3N1ZSwiX3Bmcy50c3YiLCBzZXA9IiIpLCBzZXA9Ilx0IiwgY29sLm5hbWVzID0gVCwgcm93Lm5hbWVzID0gRikKYGBgCg==